package classloader.hotload;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * Created by User on 2018/1/2.
 */
public class HotClassLoader extends ClassLoader {

    static Map<String, HotClassLoader> loaders = new Hashtable<>(2);

    String classPath;

    private HotClassLoader(String classPath) {
        super(ClassLoader.getSystemClassLoader());
        this.classPath = classPath;
    }

    public static HotClassLoader get(String classPath) {
        HotClassLoader loader = loaders.get(classPath);
        if (loader == null) {
            synchronized (HotClassLoader.class) {
                if (loader == null) {
                    loader = new HotClassLoader(classPath);
                    loaders.put(classPath, loader);
                }
            }
        }

        return loader;
    }


    public Class<?> hotLoadClass(String name) throws ClassNotFoundException {
        synchronized (classPath.intern()) {
            Class<?> findClass = findClass(name);
            if (findClass != null) return findClass;

            return super.loadClass(name);
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File classFile = new File(classPath, name.replace(".", "/") + ".class");
        if (!classFile.exists() || classFile.isDirectory()) throw new ClassNotFoundException(classFile.getAbsolutePath());
        try (FileInputStream fileInputStream = new FileInputStream(classFile)) {
            byte[] classBytes = null;
            int length;
            byte[] bs = new byte[1024];
            while ((length = fileInputStream.read(bs)) > 0) {
                if (classBytes == null) {
                    classBytes = new byte[length];
                    System.arraycopy(bs, 0, classBytes, 0, length);
                } else {
                    byte[] tmpClassBytes = new byte[classBytes.length + length];
                    System.arraycopy(classBytes, 0, tmpClassBytes, 0, classBytes.length);
                    System.arraycopy(bs, 0, tmpClassBytes, classBytes.length, length);
                    classBytes = tmpClassBytes;
                }
            }

            return this.defineClass(name, classBytes, 0, classBytes.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }
}
